/*
 * Copyright 2009 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.gtugs.web.reporting;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.datanucleus.store.appengine.query.JDOCursorHelper;
import org.gtugs.domain.Chapter;
import org.gtugs.domain.Country;
import org.gtugs.domain.Event;
import org.gtugs.domain.Topic;
import org.gtugs.repository.JdoStatsDao;
import org.gtugs.repository.PMF;
import org.gtugs.service.ChapterManager;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import com.google.appengine.api.datastore.Cursor;
import com.google.appengine.api.labs.taskqueue.Queue;
import com.google.appengine.api.labs.taskqueue.QueueFactory;
import com.google.appengine.api.labs.taskqueue.TaskOptions;

/**
 * @author jasonacooper@google.com (Jason Cooper)
 */
@Controller
public class GetEventStatsController  {

  private static final String CURSOR_KEY = "cursor";
  private static final String NOW_DATE = "now_date";
  private static final String THEN_DATE = "then_date";

  @Autowired
  private JdoStatsDao statsDao;
  @Autowired
  private ChapterManager chapterManager;

  @RequestMapping("/offline/getEventStats")
  public ModelAndView handleRequest(HttpServletRequest request,
      HttpServletResponse response) throws ServletException, IOException {
    Cursor cursor = null;
    Date nowDate = null;
    Date thenDate = null;
    if (request.getParameter(CURSOR_KEY) != null) {
      cursor = Cursor.fromWebSafeString(request.getParameter(CURSOR_KEY));
    }
    if (request.getParameter(NOW_DATE) != null) {
      nowDate = new Date(Long.parseLong(request.getParameter(NOW_DATE)));
    } else {
      nowDate = new Date();
    }
    if (request.getParameter(THEN_DATE) != null) {
      thenDate = new Date(Long.parseLong(request.getParameter(THEN_DATE)));
    } else {
      Calendar cal = Calendar.getInstance();
      cal.add(Calendar.MONTH, -6);
      thenDate = cal.getTime();
    }

    PersistenceManager pm = PMF.get().getPersistenceManager();
    Query query = pm.newQuery(Event.class);
    query.setRange(0, 20);
    query.setFilter("startDate >= startRangeParam && startDate <= endRangeParam");
    query.declareParameters("java.util.Date startRangeParam, java.util.Date endRangeParam");
    if (cursor != null) {
      Map<String, Object> extensionMap = new HashMap<String, Object>();
      extensionMap.put(JDOCursorHelper.CURSOR_EXTENSION, cursor);
      query.setExtensions(extensionMap);
    }
    List<Event> events = (List<Event>) query.execute(thenDate, nowDate);

    String cursorString =
      JDOCursorHelper.getCursor(events).toWebSafeString();
    TaskOptions options = TaskOptions.Builder
        .url("/offline/getEventStats")
        .param(CURSOR_KEY, cursorString)
        .param(NOW_DATE, ""+nowDate.getTime())
        .param(THEN_DATE, ""+thenDate.getTime());

    countEvents(request, events, options);
    groupEventsByChapterId(request, events, options);
    groupEventsByCountry(request, events, options);
    groupEventsByTopic(request, events, options);

    if (events != null && events.size() > 0) {
      Queue queue = QueueFactory.getDefaultQueue();
      queue.add(options);
    }

    query.closeAll();
    pm.close();

    return null;
  }


  private void countEvents(HttpServletRequest request,
      List<Event> events, TaskOptions options) {
    final String KEY = "eventCount";

    int eventCount = 0;
    if (request.getParameter(KEY) != null) {
      eventCount = Integer.parseInt(request.getParameter(KEY));
    }

    if (events == null || events.size() == 0) {
      statsDao.updateField(JdoStatsDao.Field.NUM_EVENTS, eventCount);
    } else {
      for (Event event : events) {
        eventCount++;
      }

      options = options.param(KEY, "" + eventCount);
    }
  }

  private void groupEventsByChapterId(HttpServletRequest request,
      List<Event> events, TaskOptions options) {
    final String KEY = "eventsGroupedByChapterId";

    JSONObject eventsGrouped = null;
    if (request.getParameter(KEY) != null) {
      eventsGrouped = (JSONObject) JSONValue.parse(request.getParameter(KEY)); 
    } else {
      eventsGrouped = new JSONObject();
    }

    if (events == null || events.size() == 0) {
      SummaryEmail summaryEmail = new SummaryEmail();

      List<Chapter> chapters = chapterManager.getActiveChapters();
      for (Chapter chapter : chapters) {
        if (eventsGrouped.containsKey(chapter.getId().toString())) {
          chapter.setNumEvents(((Long) eventsGrouped.get(chapter.getId().toString())).intValue());
        } else {
          chapter.setNumEvents(0);
        }

        GregorianCalendar threeMonthsAgo = new GregorianCalendar();
        threeMonthsAgo.add(GregorianCalendar.MONTH, -3);

        if (chapter.getStatus().equals("active")) {
          if (chapter.getNumEvents() == 0) {
            // send email
            summaryEmail.addActiveToInactive(chapter.getName());
          }
        } else if (chapter.getStatus().equals("incubating")) {
          if (chapter.getNumEvents() > 1) { // event requirements met
            boolean makeActive = true;
            if (chapter.getDescription() == null ||
                chapter.getDescription().equals("")) {
              makeActive = false;
            }
            if (chapter.getWebsite() == null ||
                chapter.getWebsite().equals("")) {
              makeActive = false;
            }
            if (chapter.getMailingList() == null ||
                chapter.getMailingList().equals("")) {
              makeActive = false;
            }

            if (makeActive) {
              chapter.setStatus("active");
              chapterManager.storeChapter(chapter);
              // send email
              summaryEmail.addIncubatingToActive(chapter.getName());
            } else {
              // send email
              summaryEmail.addIncubatingEventRqmtsMet(chapter.getName());
            }
          }
        }
      }

      summaryEmail.send();

      // Sort the array on numEvents
      for (int i = 1; i < chapters.size(); i++) {
        int numEvents = chapters.get(i).getNumEvents();
        int j = i - 1;
        boolean done = false;
        do {
          if (chapters.get(j).getNumEvents() < numEvents) {
            Chapter swapped = chapters.remove(j + 1);
            chapters.add(j, swapped);
            j--;
            if (j < 0) {
              done = true;
            }
          } else {
            done = true;
          }
        } while (!done);
      }

      statsDao.updateField(JdoStatsDao.Field.CHAPTERS_SORTED_BY_NUM_EVENTS,
          chapters);
    } else {
      for (Event event : events) {
        long eventCount = 0;
        if (eventsGrouped.containsKey(event.getChapterId().toString())) {
          eventCount = (Long) eventsGrouped.get(event.getChapterId().toString());
        }
        eventsGrouped.put(""+event.getChapterId(), ++eventCount);
      }

      options = options.param(KEY, eventsGrouped.toJSONString());
    }
  }

  private void groupEventsByCountry(HttpServletRequest request,
      List<Event> events, TaskOptions options) {
    final String KEY = "eventsGroupedByCountry";

    JSONObject eventsGrouped = null;
    if (request.getParameter(KEY) != null) {
      eventsGrouped = (JSONObject) JSONValue.parse(request.getParameter(KEY)); 
    } else {
      eventsGrouped = new JSONObject();
    }

    if (events == null || events.size() == 0) {
      Map<String, Long> countryCounts = new HashMap<String, Long>();

      List<Chapter> chapters = chapterManager.getActiveChapters();
      for (Chapter chapter : chapters) {
        if (eventsGrouped.containsKey(chapter.getId().toString())) {
          long count = 0;
          if (countryCounts.containsKey(chapter.getCountry())) {
            count = countryCounts.get(chapter.getCountry());
          }

          countryCounts.put(chapter.getCountry(), count + (Long) eventsGrouped.get(chapter.getId().toString()));
        }
      }

      List<Country> countries = new ArrayList<Country>();
      for (Map.Entry<String, Long> countryEntry : countryCounts.entrySet()) {
        Country country = new Country();
        country.setName(countryEntry.getKey());
        country.setNumEvents(countryEntry.getValue().intValue());
        countries.add(country);
      }

      // Sort the array on numEvents
      for (int i = 1; i < countries.size(); i++) {
        int numEvents = countries.get(i).getNumEvents();
        int j = i - 1;
        boolean done = false;
        do {
          if (countries.get(j).getNumEvents() < numEvents) {
            Country swapped = countries.remove(j + 1);
            countries.add(j, swapped);
            j--;
            if (j < 0) {
              done = true;
            }
          } else {
            done = true;
          }
        } while (!done);
      }

      statsDao.updateField(JdoStatsDao.Field.COUNTRIES_SORTED_BY_NUM_EVENTS,
          countries);
    } else {
      for (Event event : events) {
        long eventCount = 0;
        if (eventsGrouped.containsKey(event.getChapterId().toString())) {
          eventCount = (Long) eventsGrouped.get(event.getChapterId().toString());
        }
        eventsGrouped.put(""+event.getChapterId(), ++eventCount);
      }

      options = options.param(KEY, eventsGrouped.toJSONString());
    }
  }

  private void groupEventsByTopic(HttpServletRequest request,
      List<Event> events, TaskOptions options) {
    final String KEY = "eventsGroupedByTopic";

    JSONObject eventsGrouped = null;
    if (request.getParameter(KEY) != null) {
      eventsGrouped = (JSONObject) JSONValue.parse(request.getParameter(KEY)); 
    } else {
      eventsGrouped = new JSONObject();
    }

    if (events == null || events.size() == 0) {
      List<Topic> topics = new ArrayList<Topic>(eventsGrouped.size());

      for (Map.Entry topicEntry : (Set<Map.Entry>) eventsGrouped.entrySet()) {
        Topic topic = new Topic();
        topic.setName((String) topicEntry.getKey());
        topic.setNumEvents(((Long) topicEntry.getValue()).intValue());
        topics.add(topic);
      }

      // Sort the array on numEvents
      for (int i = 1; i < topics.size(); i++) {
        int numEvents = topics.get(i).getNumEvents();
        int j = i - 1;
        boolean done = false;
        do {
          if (topics.get(j).getNumEvents() < numEvents) {
            Topic swapped = topics.remove(j + 1);
            topics.add(j, swapped);
            j--;
            if (j < 0) {
              done = true;
            }
          } else {
            done = true;
          }
        } while (!done);
      }

      statsDao.updateField(JdoStatsDao.Field.TOPICS_SORTED_BY_NUM_EVENTS,
          topics);
    } else {
      for (Event event : events) {
        Map<String, Boolean> topics = new HashMap<String, Boolean>();
        topics.put(Topic.ANDROID, event.getTopics().contains(Topic.ANDROID));
        topics.put(Topic.GOOGLE_AJAX_APIS, event.getTopics().contains(Topic.GOOGLE_AJAX_APIS));
        topics.put(Topic.GOOGLE_APP_ENGINE, event.getTopics().contains(Topic.GOOGLE_APP_ENGINE));
        topics.put(Topic.GOOGLE_CHROME_EXTENSIONS, event.getTopics().contains(Topic.GOOGLE_CHROME_EXTENSIONS));
        topics.put(Topic.GOOGLE_EARTH_APIS, event.getTopics().contains(Topic.GOOGLE_EARTH_APIS));
        topics.put(Topic.GOOGLE_MAPS_APIS, event.getTopics().contains(Topic.GOOGLE_MAPS_APIS));
        topics.put(Topic.GOOGLE_WAVE_APIS, event.getTopics().contains(Topic.GOOGLE_WAVE_APIS));
        topics.put(Topic.GOOGLE_WEB_TOOLKIT, event.getTopics().contains(Topic.GOOGLE_WEB_TOOLKIT));
        topics.put(Topic.OPENSOCIAL, event.getTopics().contains(Topic.OPENSOCIAL));

        for (Map.Entry<String, Boolean> topicEntry : topics.entrySet()) {
          if (topicEntry.getValue() != null && topicEntry.getValue()) {
            long count = 0;
            if (eventsGrouped.containsKey(topicEntry.getKey())) {
              count = (Long) eventsGrouped.get(topicEntry.getKey());
            }
            eventsGrouped.put(topicEntry.getKey(), ++count);
          }
        }
      }

      options = options.param(KEY, eventsGrouped.toJSONString());
    }
  }
}
